Originally posted by dug
what if you've got a situation like:
>> CSessionImage **image_vector;
>> image_vector = new CSessionImage *[num_ds];
so, a dynamically sized array of pointers to objects [in this case a CSessionImage object]....
calling:
>> delete [] image_vector;
in the local deconstructor, does not slip into the CSessionImage deconstructor as expected. and so does not free the memory allocated for the CSessionImage.
any ideas?
OK, you have an array of pointers. If you want to have deletion of the array delete the objects themselves, you need to explicitly delete the objects. For example, this line:
image_vector = new CSessionImage *[num_ds];
creates ZERO objects. It creates and allocates memory for POINTERS, but the pointers don't point to anything. Thus,
delete[] image_vector;
destroys zero objects; it only undoes the effect of the new[], which was to allocate memory for pointers. The pointers are deallocated here; any objects they point to are not. So depending on how you allocate objects to be pointed at, you need to use a corresponding means to deallocate the objects.
However, remember that the two worst things to use in C++ are dynamic arrays and pointers, so dynamic arrays OF pointers are even worse. The reason they are bad is that it's very easy to leak memory in these cases. And the C++ language provides better alternatives to dynamic arrays, and other libraries provide better alternatives to pointers.
Prefer STL containers like std::vector to an array. Prefer smart pointers like Boost's boost::shared_ptr, boost::weak_ptr, std::auto_ptr, etc. over normal pointers. Be SURE to use the correct type of pointer, though.
E.g., bad code:
Code:
int num_ds = 10;
CSessionImage **image_vector;
image_vector = new CSessionImage *[num_ds];
image_vector[0] = new CSessionImage(/*...*/);
// Now, we need to delete one object and deallocate the whole array:
delete image_vector[0];
delete[] image_vector;
This is bad; we explicitly have to delete the one object we created. A slightly better idea:
Code:
int num_ds = 10;
CSessionImage **image_vector;
image_vector = new CSessionImage *[num_ds];
for (int i = 0; i < num_ds; ++i)
image_vector[i] = NULL;
image_vector[0] = new CSessionImage(/*...*/);
// Now, we need delete any objects that exist, and the array
// Deleting a pointer to NULL is a no-op by definition.
for (int i = 0; i < num_ds; ++i) delete image_vector[i];
delete[] image_vector;
However, this has problems. Say we did stuff in between:
Code:
int num_ds = 10;
CSessionImage **image_vector;
image_vector = new CSessionImage *[num_ds];
for (int i = 0; i < num_ds; ++i)
image_vector[i] = NULL;
image_vector[0] = new CSessionImage(/*...*/);
/*
Other code here
*/
CSessionImage * temp = image_vector[0];
for (int i = 0; i < num_ds; ++i) delete image_vector[i];
delete[] image_vector;
/*
Other code here
*/
temp->Function();
// Crashes because we dereference a pointer that no longer exists
The above situation happens much more often than you might think. Especially consider the case where you create an object with a dynamic array of pointers like above, and then you make a method in its public interface that allows people to get or set pointers. In this case, it can be easy to delete a pointer before you're done with it. It is also easy to lose an object; if you assign a new pointer to an array member, you need to delete the old object or it can be lost forever (you will lose the only pointer to that object and thus can never delete it).
A much, much better idea:
Code:
int num_ds = 10;
typedef boost::shared_ptr<CSessionImage> CSI_ptr;
std::vector<CSI_ptr> image_vector(num_ds);
image_vector[0] = CSI_ptr(new CSessionImage(/*...*/));
As long as you are sure to create all CSessionImage objects like this:
CSI_ptr newPtr(new CSessionImage(/*...*/));
newPtr will work (almost) just like a CSessionImage pointer; to call CSessionImage::FunctionA():
newPtr->FunctionA();
etc.
The best part? No deletes, anywhere. The vector will deallocate when it's out of scope, the pointers will destroy the object within once the last shared_ptr to that object is gone. Just a simple wrapper around every call to new guarantees you never need to worry about deletion. As long as you have a shared_ptr that is not NULL, you have a valid object pointer; the object cannot have been deleted. If you assign to the vector, and the only existing shared pointer is gone, then the object is deleted. This method solves (almost all) problems related to not being able to free memory. In fact, as long as there are no cycles of pointers (e.g., say A has a pointer to B which has a pointer to C which has a pointer to A) you cannot leak memory with boost::shared_ptr. If you need cycles, read about boost::weak_ptr.
In general:
* Use std::vector instead of dynamic arrays of all non-character objects.
* Use std::string or std::wstring for all strings.
* Use smart pointers instead of normal pointers. NEVER use auto_ptr within a container, though.